using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;

namespace Library.WebControls.Ds {
	class DataSourceWrapper : IEnumerator<DataSourceWrapper.DataItemWraper> {
		private class EmpltyDataSourceWrapper : DataSourceWrapper {
			public override bool MoveNext() {
				return false;
			}
			public override void Reset() {

			}
		}

		public static readonly DataSourceWrapper Emplty = new EmpltyDataSourceWrapper();
		private readonly IEnumerator _enumrator;
		private int _index;
		private bool _isDispose;

		public DataSourceWrapper(object dataSource) {
			if (dataSource == null) {
				throw new ArgumentNullException("DataSource");
			}
			var table = dataSource as DataTable;
			if (table != null) {
				_enumrator = table.DefaultView.GetEnumerator();
			} else {
				var listSource = dataSource as IListSource;
				if (listSource != null) {
					if (listSource.ContainsListCollection) {
						_enumrator = listSource.GetList().GetEnumerator();
					}
				} else {
					var enumrable = dataSource as IEnumerable;
					if (enumrable != null) {
						_enumrator = enumrable.GetEnumerator();
					} else {

						_enumrator = dataSource as IEnumerator;


					}
				}
			}

			if (_enumrator == null) {
				_enumrator = new EmptyEnumrator();
			}
			_enumrator.Reset();
			_index = -1;
		}

		private DataSourceWrapper() {
			if (_enumrator == null) {
				_enumrator = new EmptyEnumrator();
			}
			_index = -1;
		}

		public int Index {
			get { return _index; }
		}

		public DataItemWraper Find(string propertyName, object value) {
			Reset();
			while (MoveNext()) {
				var pValue = Current.GetPropertyValue(propertyName);
				if (pValue == null && value == null) return Current;
				if (pValue != null && pValue.Equals(value)) return Current;
			}

			return DataItemWraper.Empty;
		}

		private Object OrginalCurrent {
			get { return _enumrator.Current; }
		}

		#region IEnumerator<DataItemWraper> Members

		public DataItemWraper Current {
			get { return new DataItemWraper(_enumrator.Current); }
		}


		public void Dispose() {
			if (_isDispose) {
				return;
			}
			_index = -1;
			_isDispose = true;
			_enumrator.Reset();
			while (_enumrator.MoveNext()) {
				var dis = _enumrator.Current as IDisposable;
				if (dis != null) {
					dis.Dispose();
				}
			}
		}

		public virtual bool MoveNext() {
			if (_isDispose) {
				throw new ObjectDisposedException("DataSourceWrapper");
			}

			bool canMove = _enumrator.MoveNext();
			if (canMove) {
				_index++;
			}

			return canMove;
		}

		public virtual void Reset() {
			_index = -1;
			_enumrator.Reset();
		}

		object IEnumerator.Current {
			get { return Current; }
		}

		#endregion

		public bool MoveTo(int counter) {
			if (counter < 0) {
				throw new ArgumentException("Counter smaller zero");
			}

			while (counter >= 0) {
				if (MoveNext()) {
					counter--;
				} else {
					//throw new IndexOutOfRangeException("End of enum reached");
					return false;
				}
			}

			return true;
		}

		public DataSourceWrapper Paging(int from, int to) {
			if (from < 0 || to < 0 || from > to) {
				throw new ArgumentException(String.Format("From: {0}, To: {1}", from, to));
			}
			int counter = to - from;
			Reset();
			if (MoveTo(from)) {
				var rb = new Enumerable<object>();
				do {
					rb.AddItem(OrginalCurrent);
					counter--;
				} while (counter > 0 && MoveNext());

				return new DataSourceWrapper(rb);
			}
			return new DataSourceWrapper();
		}

		#region Nested type: DataItemWraper

		public struct DataItemWraper {

			public static readonly DataItemWraper Empty = new DataItemWraper(true, null);
			public bool IsEmpty {
				get { return _isEmpty; }
			}
			private readonly object _item;
			private readonly bool _isEmpty;

			private readonly PropertyDescriptorCollection _propertyCollection;
			public DataItemWraper(bool isEmpty, object item) {
				_item = item;
				_isEmpty = isEmpty;
				_propertyCollection = null;
			}

			public DataItemWraper(object item) {
				_propertyCollection = TypeDescriptor.GetProperties(item);
				_item = item;
				_isEmpty = false;
			}

			internal PropertyDescriptorCollection PropertyCollection {
				get { return _propertyCollection; }
			}

			public object OriginalItem {
				get { return _item; }
			}

			public object GetPropertyValue(string propertyName) {
				if (String.IsNullOrEmpty(propertyName)) {
					throw new ArgumentNullException("propertyName");
				}
				PropertyDescriptor property = _propertyCollection.Find(propertyName, true);
				if (property == null) {
					throw new ArgumentException("Not exist property: " + propertyName);
				}
				
				return property.GetValue(_item);
			}

			public void SetPropertyValue(string propertyName, object value) {
				if (String.IsNullOrEmpty(propertyName)) {
					throw new ArgumentNullException("propertyName");
				}

				PropertyDescriptor property = _propertyCollection.Find(propertyName, true);
				if (property == null) {
					throw new ArgumentException(String.Format("Not exist property: {0}", propertyName));
				}

				property.SetValue(_item, value);
			}

			public PropertyItemWrapper[] GetPropertyItemWrapperCollection() {
				var collection = new PropertyItemWrapper[_propertyCollection.Count];
				for (int i = 0; i < _propertyCollection.Count; i++) {
					collection[i] = new PropertyItemWrapper(_propertyCollection[i], OriginalItem);
				}

				return collection;
			}
		}

		#endregion

		#region Nested type: EmptyEnumrator

		private class EmptyEnumrator : IEnumerator {
			#region IEnumerator Members

			public object Current {
				get { throw new Exception("Data source is empty"); }
			}

			public bool MoveNext() {
				return false;
			}

			public void Reset() {
			}

			#endregion
		}

		#endregion

		#region Nested type: PropertyItemWrapper

		public struct PropertyItemWrapper {
			private readonly object _original;
			private readonly PropertyDescriptor _propertyDescriptor;

			internal PropertyItemWrapper(PropertyDescriptor propertyDescriptor, object original) {
				_propertyDescriptor = propertyDescriptor;
				_original = original;
			}

			public string PropertyName {
				get { return _propertyDescriptor.Name; }
			}

			public Type PropertyType {
				get { return _propertyDescriptor.PropertyType; }
			}

			public void SetValue(object value) {
				_propertyDescriptor.SetValue(_original, value);
			}

			public object GetValue() {
				return _propertyDescriptor.GetValue(_original);
			}
		}

		#endregion
	}
}